﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Xml;

namespace TimeSheetNote
{

    class Gl
    {

        static int GetNextID()
        {
            // выбор максимального id и +1
            int ret = 1;
            foreach (TaskEl tsk in TL) if (ret < tsk.ID) ret = tsk.ID;
            return ret + 1;
        }

        public static List<TaskEl> TL = new List<TaskEl>();

        public static string dPathSave;
        public static string dPathBackups;

        public static int ActVerDB = 0;

        public static TaskEl AddTaskInDB(string Text)
        {
            TaskEl tsk = new TaskEl(GetNextID(), Text);
            TL.Add(tsk);
            SaveDB();
            return tsk;
        }

        /// <summary>Выбор из базы данных задач только для этого дня</summary>
        public static List<TaskEl> GetDayTasks(DateTime dt)
        {
            List<TaskEl> ret = new List<TaskEl>();
            for (int i = 0; i < TL.Count; i++) if (TL[i].Days.ContainsKey(dt)) ret.Add(TL[i]);
            return ret;
        }

        /// <summary>Выбор из базы данных задач только не закрытых</summary>
        public static List<TaskEl> GetNotCloseTasks()
        {
            List<TaskEl> ret = new List<TaskEl>();
            for (int i = 0; i < TL.Count; i++) if (!TL[i].IsClose) ret.Add(TL[i]);
            return ret;
        }

        /// <summary>Выбор из базы данных задач только закрытых</summary>
        public static List<TaskEl> GetCloseTasks()
        {
            List<TaskEl> ret = new List<TaskEl>();
            for (int i = 0; i < TL.Count; i++) if (TL[i].IsClose) ret.Add(TL[i]);
            return ret;
        }

        /* public static List<TaskEl> Load(string fPath)
         {
             List<TaskEl> tl = new List<TaskEl>();
             if (!File.Exists(fPath)) return tl;

             try
             {
                 XmlDocument doc = new XmlDocument();
                 doc.Load(fPath);
                 XmlNode root = doc.GetElementsByTagName("SAVE")[0];

                 // задел на версионность
                 int LoadVer = Convert.ToInt32(root.Attributes["Vertion"].Value);

                 XmlNodeList ndl = doc.GetElementsByTagName("Task");
                 foreach (XmlNode nd in ndl)
                 {
                     tl.Add(new TaskEl(nd));
                 }
             }
             catch (Exception ex) { MessageBox.Show("Ошибка загрузки: " + ex.Message); }

             return tl;
         }*/

        public static void LoadDB()
        {
            Gl.TL.Clear();
            if (File.Exists(Gl.dPathSave + "\\DBTasks.xml"))
            {
                try
                {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(Gl.dPathSave + "\\DBTasks.xml");
                    XmlNode root = doc.GetElementsByTagName("SAVE")[0];

                    // задел на версионность
                    int LoadVer = Convert.ToInt32(root.Attributes["Vertion"].Value);

                    XmlNodeList ndl = doc.GetElementsByTagName("Task");
                    foreach (XmlNode nd in ndl)
                    {
                        Gl.TL.Add(new TaskEl(nd));
                    }

                }
                catch (Exception ex) { MessageBox.Show("Ошибка загрузки: " + ex.Message); }
            }

            // добавление пресетов в БД (если их нет)
            foreach (TaskEl tsk in Presets)
            {
                if (GetTaskByID(tsk.ID) == null)
                    TL.Add(tsk);
            }

        }

        public static void SaveDB()
        {
            SaveDB(Gl.dPathSave + "\\DBTasks.xml");
        }

        public static void SaveDBBackup()
        {
            SaveDB(Gl.dPathBackups + "\\DBTasks_" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".xml");
        }

        static void SaveDB(string fPath)
        {
            XmlDocument doc = new XmlDocument();
            XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "utf-8", null);
            doc.InsertBefore(xmlDeclaration, doc.DocumentElement);

            //Родительский тег
            XmlElement rootNode = doc.CreateElement("SAVE"); doc.AppendChild(rootNode);
            rootNode.SetAttribute("Vertion", ActVerDB.ToString());

            foreach (TaskEl tsk in TL) rootNode.AppendChild(tsk.GetSaveXml(doc));

            doc.Save(fPath);
        }

        public static TaskEl GetTaskByID(int Id)
        {
            for (int i = 0; i < Gl.TL.Count; i++) if (Gl.TL[i].ID == Id) { return Gl.TL[i]; }
            return null;
        }

        public static List<TaskEl> Presets = new List<TaskEl>()
        {
            new TaskEl(-1, "Анализ и обсуждение задач"),
            new TaskEl(-2, "Консультация"),
            new TaskEl(-3, "Учет рабочего времени")
        };

        public static void ConvertOldSaves()
        {
            TL.Clear();
            int nID = 1;

            string[] fl = Directory.GetFiles(dPathSave + "\\Saves", "*.xml");
            foreach (string fPath in fl)
            {
                try
                {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(fPath);
                    XmlNode root = doc.GetElementsByTagName("SAVE")[0];

                    // задел на версионность
                    int LoadVer = Convert.ToInt32(root.Attributes["Vertion"].Value);

                    XmlNodeList ndl = doc.GetElementsByTagName("Task");
                    foreach (XmlNode nd in ndl)
                    {
                        DateTime actDay = Convert.ToDateTime((new FileInfo(fPath)).Name.Replace("save_", "").Replace(".xml", "").Replace('_', '.') + " 00:00:00");
                        string text = nd.InnerText;
                        int time = Convert.ToInt32(nd.Attributes["Time"].Value);

                        int ind = -1;
                        for (int i = 0; i < TL.Count; i++) if (TL[i].Text.ToUpper().Trim() == text.ToUpper().Trim()) { ind = i; break; }

                        if (ind == -1) { TL.Add(new TaskEl(nID, text)); nID++; ind = TL.Count - 1; }

                        if (TL[ind].Days.ContainsKey(actDay)) TL[ind].Days[actDay] += time;
                        else TL[ind].Days.Add(actDay, time);

                        TL[ind].IsClose = nd.Attributes["IsClose"].Value == "true";
                    }
                }
                catch (Exception ex) { MessageBox.Show("Ошибка загрузки: " + ex.Message); }
            }

            SaveDB();
        }

        /// <summary>Удаление задач, для которых не задано ни одного дня (может быть при удалении)</summary>
        public static void DeleteEmptyTasks()
        {
            for (int i = 0; i < TL.Count; i++) if (TL[i].Days.Count == 0 && TL[i].ID > 0) { TL.RemoveAt(i); i--; }
        }
    }

    class TaskEl : IComparable<TaskEl>
    {
        public int ID;
        public string Text;
        public bool IsClose;
        public Dictionary<DateTime, int> Days = new Dictionary<DateTime, int>();

        public int TotalTime { get { int ret = 0; foreach (KeyValuePair<DateTime, int> day in Days) ret += day.Value; return ret; } }

        public TaskEl(int ID, string Text)
        {
            this.ID = ID;
            this.Text = Text;
            this.IsClose = false;
        }

        public TaskEl(XmlNode TaskElm)
        {
            ID = Convert.ToInt32(TaskElm.Attributes["ID"].Value);
            IsClose = TaskElm.Attributes["IsClose"].Value == "true";

            XmlNode ndTxt = TaskElm.SelectSingleNode("Text");
            Text = ndTxt.InnerText;

            XmlNodeList nl = TaskElm.SelectNodes("Day");
            foreach (XmlNode node in nl) Days.Add(Convert.ToDateTime(node.Attributes["DayDate"].Value + " 00:00:00"), Convert.ToInt32(node.Attributes["elTime"].Value));
        }

        public XmlElement GetSaveXml(XmlDocument doc)
        {
            XmlElement taskNode = doc.CreateElement("Task");
            taskNode.SetAttribute("IsClose", IsClose ? "true" : "false");
            taskNode.SetAttribute("ID", ID.ToString());

            XmlNode ndText = doc.CreateElement("Text");
            ndText.InnerText = Text;
            taskNode.AppendChild(ndText);

            foreach (KeyValuePair<DateTime, int> day in Days)
            {
                XmlElement ndDay = doc.CreateElement("Day");
                ndDay.SetAttribute("DayDate", day.Key.ToString("dd.MM.yyyy"));
                ndDay.SetAttribute("elTime", day.Value.ToString());
                taskNode.AppendChild(ndDay);
            }

            return taskNode;
        }

        public DateTime GetFirstDay()
        {
            if (Days.Count == 0) return DateTime.MinValue;
            DateTime ret = DateTime.MaxValue;
            foreach (KeyValuePair<DateTime, int> day in Days) if (ret > day.Key) ret = day.Key;
            return ret;
        }

        public DateTime GetLastDay()
        {
            if (Days.Count == 0) return DateTime.MinValue;
            DateTime ret = DateTime.MinValue;
            foreach (KeyValuePair<DateTime, int> day in Days) if (ret < day.Key) ret = day.Key;
            return ret;
        }

        public enum CompareMode { None, ID, Text, LastDate, FirstDate }
        public static CompareMode CmprMode = CompareMode.None;

        public int CompareTo(TaskEl other)
        {
            switch (CmprMode)
            {
                case CompareMode.ID: return ID.CompareTo(other.ID);
                case CompareMode.Text: return Text.CompareTo(other.Text);
                case CompareMode.LastDate: return GetLastDay().CompareTo(other.GetLastDay());
                case CompareMode.FirstDate: return GetFirstDay().CompareTo(other.GetFirstDay());
                default: return 0;
            }
        }
    }

    class TaskCntrl : IDisposable
    {
        Control ParentControl;
        TextBox textBox1 = new TextBox();
        NumericUpDown numericUpDown1 = new NumericUpDown();
        NumericUpDown numericUpDown2 = new NumericUpDown();
        Panel panel1 = new Panel();
        ToolStrip toolStrip1 = new ToolStrip();
        ToolStripButton tBtnStartStop = new ToolStripButton();
        Label lblSec = new Label();
        Label lblID = new Label();
        Button btnDel = new Button();
        Button btnClose = new Button();

        bool bSet = false;

        DateTime actDay;

        public TaskEl Tsk;

        #region *** свойства ***********************************************************

        public string TxbText { get { return textBox1.Text; } set { bSet = true; textBox1.Text = value; Tsk.Text = TxbText; bSet = false; } }

        public int Bottom { get { return panel1.Bottom; } }

        public int Time
        {
            get { return (int)numericUpDown1.Value * 3600 + (int)numericUpDown2.Value * 60 + Sec; }
            set
            {
                bSet = true;
                numericUpDown1.Value = value / 3600;
                numericUpDown2.Value = (value - (int)numericUpDown1.Value * 3600) / 60;
                Sec = value % 60;

                Tsk.Days[actDay] = Time;

                bSet = false;
            }
        }

        public int Sec { get { return Convert.ToInt32(lblSec.Text); } set { lblSec.Text = value.ToString("00"); } }

        int ind;
        public int Ind { get { return ind; } set { ind = value; panel1.Top = GetPosY(ind); } }

        public bool IsRun { get { return tBtnStartStop.Checked; } }

        bool bClose = false;
        public bool IsClose { get { return bClose; } set { bClose = value; SetClose(); } }

        int startTime;
        DateTime startDate;

        #endregion *** свойства ***********************************************************

        #region *** события ******************************************

        public event EventHandler UserModifyText;
        void OnUserModifyText() { if (UserModifyText != null) UserModifyText(this, new EventArgs()); }

        public event EventHandler UserModifyTime;
        void OnUserModifyTime() { if (UserModifyTime != null) UserModifyTime(this, new EventArgs()); }

        public event EventHandler UserStartStop;
        void OnUserStartStop() { if (UserStartStop != null) UserStartStop(this, new EventArgs()); }

        public event EventHandler DeleteClick;
        void OnDeleteClick() { if (DeleteClick != null) DeleteClick(this, new EventArgs()); }

        public event EventHandler CloseClick;
        void OnCloseClick() { if (CloseClick != null) CloseClick(this, new EventArgs()); }

        #endregion *** события ******************************************

        #region *** конструкторы ***********************************************************

        public TaskCntrl(Control ParentControl, int Ind, TaskEl Tsk, DateTime ActDay)
        {
            this.ParentControl = ParentControl;

            this.ind = Ind;
            this.Tsk = Tsk;
            AddControls();
            this.TxbText = Tsk.Text;
            this.actDay = ActDay;
            this.Time = Tsk.Days[actDay];
            bClose = Tsk.IsClose;
            SetClose();
            lblID.Text = Tsk.ID.ToString();
        }

        void AddControls()
        {
            ParentControl.SuspendLayout();

            textBox1.Location = new Point(35, 0);
            textBox1.Multiline = true;
            textBox1.Size = new Size(360, 60);
            textBox1.TextChanged += new EventHandler(txb_TextChanged);
            textBox1.TabStop = false;

            numericUpDown1.Location = new Point(400, 12);
            numericUpDown1.Size = new Size(38, 20);
            numericUpDown1.Value = 0;
            numericUpDown1.ValueChanged += new EventHandler(num_TextChanged);

            numericUpDown2.Location = new Point(440, 12);
            numericUpDown2.Size = new Size(38, 20);
            numericUpDown2.Value = 0;
            numericUpDown2.ValueChanged += new EventHandler(num_TextChanged);
            numericUpDown1.Maximum = 59;

            lblSec.Location = new Point(477, 15);
            lblSec.Size = new Size(20, 20);
            lblSec.Text = "00";

            toolStrip1.Dock = DockStyle.None;
            toolStrip1.GripStyle = ToolStripGripStyle.Hidden;
            toolStrip1.Items.AddRange(new ToolStripItem[] { this.tBtnStartStop });
            toolStrip1.Location = new Point(2, 15);
            toolStrip1.Size = new Size(55, 25);

            tBtnStartStop.DisplayStyle = ToolStripItemDisplayStyle.Image;
            //toolStripButton1.Image = ((Image)(resources.GetObject("toolStripButton1.Image")));
            tBtnStartStop.ImageTransparentColor = Color.Magenta;
            tBtnStartStop.Size = new Size(23, 22);
            tBtnStartStop.ToolTipText = "Run";
            tBtnStartStop.Click += new EventHandler(tBtnStartStop_CheckedChanged);
            tBtnStartStop.Image = Properties.Resources.run2;

            btnDel.Location = new Point(487, -3);
            btnDel.Size = new Size(16, 16);
            btnDel.Image = Properties.Resources.delete;
            btnDel.Click += new EventHandler(BtnDel_Click);

            btnClose.Location = new Point(-2, 38);
            btnClose.Size = new Size(22, 22);
            btnClose.TabStop = false;
            btnClose.Image = Properties.Resources.Open;
            btnClose.Click += new EventHandler(BtnClose_Click);

            lblID.BorderStyle = BorderStyle.FixedSingle;
            lblID.AutoSize = false;
            lblID.BackColor = Color.AliceBlue;
            lblID.Location = new Point(0, 0);
            lblID.TextAlign = ContentAlignment.TopCenter;
            lblID.Size = new Size(37, 15);
            lblID.Text = "0000";

            panel1.Controls.Add(textBox1);
            if (Tsk.ID > 0) panel1.Controls.Add(btnClose);
            panel1.Controls.Add(lblID);
            panel1.Controls.Add(toolStrip1);
            panel1.Controls.Add(numericUpDown2);
            panel1.Controls.Add(numericUpDown1);
            panel1.Controls.Add(lblSec);
            panel1.Controls.Add(btnDel);
            panel1.Location = new Point(12, GetPosY(ind));
            panel1.Size = new Size(500, 60);
            panel1.BorderStyle = BorderStyle.FixedSingle;

            textBox1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;

            numericUpDown1.Anchor = AnchorStyles.Top | AnchorStyles.Right;
            numericUpDown2.Anchor = AnchorStyles.Top | AnchorStyles.Right;
            lblSec.Anchor = AnchorStyles.Top | AnchorStyles.Right;
            btnDel.Anchor = AnchorStyles.Top | AnchorStyles.Right;

            panel1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;

            panel1.Width = ParentControl.ClientRectangle.Width - 20;

            ParentControl.ResumeLayout();

            ParentControl.Controls.Add(panel1);
        }

        #endregion *** конструкторы ***********************************************************

        int GetPosY(int ind)
        {
            return 12 + 65 * ind;
        }

        private void txb_TextChanged(object sender, EventArgs e)
        {
            if (bSet) return;
            Tsk.Text = TxbText;
            OnUserModifyText();
        }

        private void num_TextChanged(object sender, EventArgs e)
        {
            if (bSet) return;
            if (IsRun) Start();
            Tsk.Days[actDay] = Time;
            OnUserModifyTime();
        }

        private void tBtnStartStop_CheckedChanged(object sender, EventArgs e)
        {
            if (bSet) return;
            if (IsRun) { Stop(); }
            else { Start(); }
            OnUserStartStop();
        }

        private void BtnDel_Click(object sender, EventArgs e)
        {
            OnDeleteClick();
        }

        private void BtnClose_Click(object sender, EventArgs e)
        {
            IsClose = !IsClose;
            SetClose();
            OnCloseClick();
        }

        void SetClose()
        {
            if (bClose)
            {
                if (IsRun) Stop();
                btnClose.Image = Properties.Resources.Close;
                toolStrip1.Visible = false;
                textBox1.ReadOnly = true;
                textBox1.BackColor = SystemColors.Control;
            }
            else
            {
                btnClose.Image = Properties.Resources.Open;
                toolStrip1.Visible = true;
                textBox1.ReadOnly = false;
                textBox1.BackColor = SystemColors.Window;
            }

            Tsk.IsClose = IsClose;
        }

        public void Start()
        {
            bSet = true;
            startTime = Time;
            startDate = DateTime.Now;
            tBtnStartStop.Checked = true;
            bSet = false;
            tBtnStartStop.Image = Properties.Resources.run1;
            textBox1.BackColor = SystemColors.Info;
            numericUpDown1.BackColor = SystemColors.Info;
            numericUpDown2.BackColor = SystemColors.Info;
        }

        public void Stop()
        {
            Tick();

            bSet = true;
            tBtnStartStop.Checked = false;
            bSet = false;
            tBtnStartStop.Image = Properties.Resources.run2;
            textBox1.BackColor = SystemColors.Window;
            numericUpDown1.BackColor = SystemColors.Window;
            numericUpDown2.BackColor = SystemColors.Window;
        }

        /// <summary>Обновление времени запущенной задачи</summary>
        public void Tick()
        {
            if (!IsRun) return;
            Time = startTime + (int)(DateTime.Now - startDate).TotalSeconds;
            Tsk.Days[actDay] = Time;
        }

        public void Dispose()
        {
            ParentControl.Controls.Remove(panel1);
            panel1.Dispose();
        }
    }

}
